package runner.SAPSO_VM;


import org.cloudbus.cloudsim.Host;
import org.cloudbus.cloudsim.Vm;
import org.cloudbus.cloudsim.power.PowerHost;
import runner.SAPSO_VM.log.LOG;
import runner.jswarm_pso.Particle;
import runner.jswarm_pso.Swarm;
import runner.utils.Calculator;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class SAPSO {
    private static Swarm swarm;
    private static SchedulerParticle particles[];
    private static SchedulerFitnessFunction ff;
    List<Vm> vmList = new ArrayList<>();
    List<PowerHost> hostList = new ArrayList<>();
    public static int iter = 0;

    public SAPSO(List<Vm> vmList, List<PowerHost> hostList) {
        this.vmList = vmList;
        this.hostList = hostList;
        initParticles(vmList, hostList);
        ff = new SchedulerFitnessFunction(vmList, hostList);
        swarm = new Swarm(particles.length, new SchedulerParticle(vmList, hostList), ff);
        double bestFitness = swarm.getBestFitness();
        System.out.println(bestFitness);
    }


    public double[] run() {

        // 学习因子
        double c_max = 2.5, c_min = 1.25;
        double w = 0.9;

        swarm.setMinPosition(0);
        swarm.setMaxPosition(hostList.size() - 1);
        swarm.setMaxMinVelocity(0.5);
        swarm.setParticles(particles);
        swarm.setParticleUpdate(new SchedulerParticleUpdate(new SchedulerParticle(vmList, hostList)));

        for (int i = 1; i <= Constants.NO_OF_Iterations; i++) {
            iter = i;
            double c1 = c_max - (i*(c_max-c_min)) / Constants.NO_OF_Iterations;
            double c2 = c_min + (i*(c_max-c_min)) / Constants.NO_OF_Iterations;

            swarm.setC1(c1);
            swarm.setC2(c2);

            swarm.evolve();
//            if (i % 10 == 0) {
                System.out.printf("Gloabl best at iteration (%d): %f\n", i, swarm.getBestFitness());
//            }

//            String output = "C:/Users/smm/Desktop/cloudsim-package/cloudsim-package/output";
//            PrintStream fitBestSteram = LOG.getPrintStream(output + "/" + "bestFitSet" + ".txt", true);
//            LOG.append(fitBestSteram, "%f\n", swarm.getBestFitness());
//            LOG.closeLog(fitBestSteram);

            Particle[] particles = swarm.getParticles();
            double hbest = 0.0, hmax = 0.0, hmin = 0.0, h = 0.0;
            double[] distance = new double[particles.length];
            for (int p=0; p<particles.length; p++) {
                double[] position = particles[p].getPosition();
                double[] bestPostion = swarm.getBestPosition();
                double distancei = 0.0;
                for (int j=0; j<particles[p].getPosition().length; j++) {
                    distancei += Math.sqrt(Math.pow(bestPostion[j] - position[j], 2));
                }
                distance[p] = distancei;
                hbest += distancei;
            }
            hbest /= particles.length - 1;
            Arrays.sort(distance);
            hmax = distance[particles.length - 1];
            hmin = distance[0];
            h = (hbest - hmin) / (hmax - hmin);

            w = 1 / (1 + 1.5*Math.pow(Math.E, -2.6*h));

//            w = CalSOW(hmax, hmin, i, swarm.getInertia());

            swarm.setInertia(w);
        }
//        System.out.println("\nThe best fitness value: " + swarm.getBestFitness() + "\nBest makespan: " + ff.calcMakespan(swarm.getBestParticle().getBestPosition()));
//        System.out.println("The best totalcost:"+ff.calcTotalTime(swarm.getBestParticle().getPosition()));
        System.out.println("The best solution is: ");
//        SchedulerParticle bestParticle = (SchedulerParticle) swarm.getBestParticle();
//        System.out.println(bestParticle.toString());

        return swarm.getBestPosition();
    }

    private double GetWeightBySAStrategy(double w,int iter)
    {
        double value = 0.0;
//        使用模拟退火的策略
        return value;
    }

    private double CalSOW(double w_max, double w_min, int i, double w) {
        double iter = i+1;//当前迭代的次数
        double ret = Calculator.div(iter, Constants.NO_OF_Iterations);
        if(iter>=1&iter<=Constants.NO_OF_Iterations/2) {
            w= -Math.pow(ret,2.0)+w_max;
        }
        if(iter<=Constants.NO_OF_Iterations&&iter>Constants.NO_OF_Iterations/2) {
            w= -Math.pow(ret-1.0,2.0)+w_min;
        }
        if(w>w_max) {
            w = w_max;
        }
        if(w<w_min) {
            w = w_min;
        }
        return w;
    }

    private static void initParticles(List<Vm> vmList, List<PowerHost> hostList) {
        particles = new SchedulerParticle[Constants.POPULATION_SIZE];
        for (int i = 0; i < Constants.POPULATION_SIZE; ++i)
            particles[i] = new SchedulerParticle(vmList, hostList);
    }

    public void printBestFitness() {
//        System.out.println("\nBest fitness value: " + swarm.getBestFitness() +
//                "\nBest makespan: " + ff.calcMakespan(swarm.getBestParticle().getBestPosition()));
    }
}
